<?php
/**
 *   Accept log messages, add necessary info (Process ID, microtime),
 *      and forward to designated Log_Writers
 *  The default log writers are set up in start()
 *
 */
class Yz_Log_Log
    extends Yz_Class
    implements Yz_Log_Interface
{


    /**
    *   @var self
    */
    protected static $instance;

    /**
    *   @var (string)
    */
    protected $logId;

    /**
    *   The minimum verbosity level when it is necessary to generate log message
    *   @var int
    */
    protected $minCode = self::FATAL;

    /** the list of writers along with the verbosity codes
    *   @var array : array(writer, code)
    */
    protected $writers = array();


    /******************************
    ***      Public methods     ***
    *******************************/


    /**
     *   @return instance
     */
    public static function init()
    {
        $i =& self::$instance;
        if (! $i) {
            $i = new static();
            $i->start();
        }
        return 1;
    }

    /** add (or replace) a log writer
     *      If the new log writer's signature is already registered, replace old with new
    *   @param Yz_Log_Writer_Interface writer
    *   @param int code : the maximum verbosity level at which the writer will write
    *   @return int=0
    */
    public static function addWriter( Yz_Log_Writer_Interface $wr, $code=self::FATAL )
    {
        $log = self::$instance;
        $wr->setLogId($log->logId);
        $sig = "$wr";
        $log->writers[$sig] = array($wr, $code);
        // update minimum verbosity level
        if ( $code > $log->minCode )
            $log->minCode = $code;
        if ($code >= self::INFO)
            $wr->write( "BEGIN LOG at " . date('Ymd His', YZ_TIME) );
        return 0;
    }


    /**
     *  @param mixed $message
     *  @param int $code
     *  @param int|null $traceDepth
     *  @return int
     */
    public static function log( $message, $code=self::DEBUG4, $traceDepth=0 )
    {
        $log = self::$instance;

        if ( ! $log->writers )
            return 2;

        // don't generate message if the code is more verbose than any of the registered writers
        if( $code > $log->minCode )
            return 1;

        $trace = debug_backtrace();
        // the caller has its trace at level [1]
        $ct = isset( $trace[$traceDepth+1] ) ? $trace[$traceDepth+1] : $trace[1];

        switch ( true ) {
            case array_key_exists( 'object', $ct ) :
                $text = get_class($ct['object']) . '.' . $ct['function'] . '()';
                break;
            case array_key_exists( 'class', $ct ) :
                $text = $ct['class'] . '::' . $ct['function'] . '()';
                break;
            case array_key_exists( 'function', $ct ) :
                $text = 'function ' . $ct['function'] . '()';
                break;
            default:
                $text = $ct['file'] . '@' . $ct['line'];
        }

        "" !== "$message" and $text .= ": " . $message;
        foreach ( $log->writers as $entry ) {
            list ($wr, $wrCode) = $entry;
            if ( $wrCode >= $code )
                $wr->write( $text );
        }

        return 0;
    } // log



    /**
     *
     */
    protected function __construct()
    {
        $logId = $this->identifyLogId();
        if ( null === $logId )
            $logId = YZ_TIME;
        $this->logId = $logId;
    }



    /** Setup default log writers
     *  Override this function if other writers required
     *
     */
    protected function start()
    {
        $this->addWriter(
            new Yz_Log_Writer_StdErr(),
            self::ERROR
        );
    }


    /**
    *
    */
    protected function identifyLogId()
    {
        // read LOGID from file
        $idfile = YZ_LIB . 'app-id.log';
        if ( is_file($idfile) && is_writable($idfile) ) {
            $id = file_get_contents($idfile);
            $id = 1 + (int)$id;
            file_put_contents($idfile, $id);
            return $id;
        }
        return null;
    }


} // class


